/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the  "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * $Id$
 */
package wx.xml.xalan.xalan.templates;

import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Vector;

import javax.xml.transform.ErrorListener;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;

import wx.xml.xalan.xalan.extensions.ExtensionNamespacesManager;
import wx.xml.xalan.xalan.processor.XSLTSchema;
import wx.xml.xalan.xalan.res.XSLMessages;
import wx.xml.xalan.xalan.res.XSLTErrorResources;
import wx.xml.xalan.xalan.transformer.TransformerImpl;
import wx.xml.xalan.xml.dtm.DTM;
import wx.xml.xalan.xml.dtm.ref.ExpandedNameTable;
import wx.xml.xalan.xml.utils.IntStack;
import wx.xml.xalan.xml.utils.QName;
import wx.xml.xalan.xpath.XPath;
import wx.xml.xalan.xpath.XPathContext;

/**
 * This class represents the root object of the stylesheet tree.
 *
 * @xsl.usage general
 */
public class StylesheetRoot extends StylesheetComposed
    implements java.io.Serializable, Templates {
    static final long serialVersionUID = 3875353123529147855L;
    /**
     * Used for default selection.
     *
     * @serial
     */
    XPath m_selectDefault;
    /**
     * The flag for the setting of the optimize feature;
     */
    private boolean m_optimizer = true;
    /**
     * The flag for the setting of the incremental feature;
     */
    private boolean m_incremental = false;
    /**
     * The flag for the setting of the source_location feature;
     */
    private boolean m_source_location = false;
    /**
     * State of the secure processing feature.
     */
    private boolean m_isSecureProcessing = false;
    /**
     * The schema used when creating this StylesheetRoot
     *
     * @serial
     */
    private HashMap m_availElems;
    private transient ExtensionNamespacesManager m_extNsMgr = null;
    /**
     * The combined list of imports.  The stylesheet with the highest
     * import precedence will be at element 0.  The one with the lowest
     * import precedence will be at element length - 1.
     *
     * @serial
     */
    private StylesheetComposed[] m_globalImportList;
    /**
     * This will be set up with the default values, and then the values
     * will be set as stylesheets are encountered.
     *
     * @serial
     */
    private OutputProperties m_outputProperties;
    /**
     * Flag indicating whether an output method has been set by the user.
     *
     * @serial
     */
    private boolean m_outputMethodSet = false;
    /**
     * Composed set of all included and imported attribute set properties.
     * Each entry is a vector of ElemAttributeSet objects.
     *
     * @serial
     */
    private HashMap m_attrSets;
    /**
     * Table of DecimalFormatSymbols, keyed by QName.
     *
     * @serial
     */
    private Hashtable m_decimalFormatSymbols;
    /**
     * A list of all key declarations visible from this stylesheet and all
     * lesser stylesheets.
     *
     * @serial
     */
    private Vector m_keyDecls;
    /**
     * Composed set of all namespace aliases.
     *
     * @serial
     */
    private Hashtable m_namespaceAliasComposed;

/*
  public void runtimeInit(TransformerImpl transformer) throws TransformerException
  {
    System.out.println("StylesheetRoot.runtimeInit()");
      
  //    try{throw new Exception("StylesheetRoot.runtimeInit()");} catch(Exception e){e.printStackTrace();}

    }
*/

    //============== Templates Interface ================
    /**
     * The "xsl:template" properties.
     *
     * @serial
     */
    private TemplateList m_templateList;
    /**
     * Composed set of all variables and params.
     *
     * @serial
     */
    private Vector m_variables;
    /**
     * A list of properties that specify how to do space
     * stripping. This uses the same exact mechanism as Templates.
     *
     * @serial
     */
    private TemplateList m_whiteSpaceInfoList;

    //============== End Templates Interface ================
    /**
     * The default template to use for text nodes if we don't find
     * anything else.  This is initialized in initDefaultRule().
     *
     * @serial
     * @xsl.usage advanced
     */
    private ElemTemplate m_defaultTextRule;
    /**
     * The default template to use if we don't find anything
     * else.  This is initialized in initDefaultRule().
     *
     * @serial
     * @xsl.usage advanced
     */
    private ElemTemplate m_defaultRule;
    /**
     * The default template to use for the root if we don't find
     * anything else.  This is initialized in initDefaultRule().
     * We kind of need this because the defaultRule isn't good
     * enough because it doesn't supply a document context.
     * For now, I default the root document element to "HTML".
     * Don't know if this is really a good idea or not.
     * I suspect it is not.
     *
     * @serial
     * @xsl.usage advanced
     */
    private ElemTemplate m_defaultRootRule;
    /**
     * The start rule to kick off the transformation.
     *
     * @serial
     * @xsl.usage advanced
     */
    private ElemTemplate m_startRule;
    private transient ComposeState m_composeState;
    private String m_extensionHandlerClass =
        "wx.xml.xalan.xalan.extensions.ExtensionHandlerExsltFunction";

    /**
     * Uses an XSL stylesheet document.
     *
     * @throws TransformerConfigurationException if the baseIdentifier can not be resolved to a URL.
     */
    public StylesheetRoot(ErrorListener errorListener) throws TransformerConfigurationException {

        super(null);

        setStylesheetRoot(this);

        try {
            m_selectDefault = new XPath("node()", this, this, XPath.SELECT, errorListener);

            initDefaultRule(errorListener);
        } catch (TransformerException se) {
            throw new TransformerConfigurationException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_INIT_DEFAULT_TEMPLATES, null), se); //"Can't init default templates!", se);
        }
    }

    /**
     * Creates a StylesheetRoot and retains a pointer to the schema used to create this
     * StylesheetRoot.  The schema may be needed later for an element-available() function call.
     *
     * @param schema The schema used to create this stylesheet
     * @throws TransformerConfigurationException if the baseIdentifier can not be resolved to a URL.
     */
    public StylesheetRoot(XSLTSchema schema, ErrorListener listener) throws TransformerConfigurationException {

        this(listener);
        m_availElems = schema.getElemsAvailable();
    }

    /**
     * Tell if this is the root of the stylesheet tree.
     *
     * @return True since this is the root of the stylesheet tree.
     */
    public boolean isRoot() {
        return true;
    }

    /**
     * Return the state of the secure processing feature.
     */
    public boolean isSecureProcessing() {
        return m_isSecureProcessing;
    }

    /**
     * Set the state of the secure processing feature.
     */
    public void setSecureProcessing(boolean flag) {
        m_isSecureProcessing = flag;
    }

    /**
     * Get the hashtable of available elements.
     *
     * @return table of available elements, keyed by qualified names, and with
     * values of the same qualified names.
     */
    public HashMap getAvailableElements() {
        return m_availElems;
    }

    /**
     * Only instantiate an ExtensionNamespacesManager if one is called for
     * (i.e., if the stylesheet contains  extension functions and/or elements).
     */
    public ExtensionNamespacesManager getExtensionNamespacesManager() {
        if (m_extNsMgr == null)
            m_extNsMgr = new ExtensionNamespacesManager();
        return m_extNsMgr;
    }

    /**
     * Get the vector of extension namespaces. Used to provide
     * the extensions table access to a list of extension
     * namespaces encountered during composition of a stylesheet.
     */
    public Vector getExtensions() {
        return m_extNsMgr != null ? m_extNsMgr.getExtensions() : null;
    }

    /**
     * Create a new transformation context for this Templates object.
     *
     * @return A Transformer instance, never null.
     */
    public Transformer newTransformer() {
        return new TransformerImpl(this);
    }

    public Properties getDefaultOutputProps() {
        return m_outputProperties.getProperties();
    }

    /**
     * Get the static properties for xsl:output.  The object returned will
     * be a clone of the internal values, and thus it can be mutated
     * without mutating the Templates object, and then handed in to
     * the process method.
     *
     * <p>For XSLT, Attribute Value Templates attribute values will
     * be returned unexpanded (since there is no context at this point).</p>
     *
     * @return A Properties object, not null.
     */
    public Properties getOutputProperties() {
        return (Properties) getDefaultOutputProps().clone();
    }

    /**
     * Recompose the values of all "composed" properties, meaning
     * properties that need to be combined or calculated from
     * the combination of imported and included stylesheets.  This
     * method determines the proper import precedence of all imported
     * stylesheets.  It then iterates through all of the elements and
     * properties in the proper order and triggers the individual recompose
     * methods.
     *
     * @throws TransformerException
     */
    public void recompose() throws TransformerException {
        // Now we make a Vector that is going to hold all of the recomposable elements

        Vector recomposableElements = new Vector();

        // First, we build the global import tree.

        if (null == m_globalImportList) {

            Vector importList = new Vector();

            addImports(this, true, importList);

            // Now we create an array and reverse the order of the importList vector.
            // We built the importList vector backwards so that we could use addElement
            // to append to the end of the vector instead of constantly pushing new
            // stylesheets onto the front of the vector and having to shift the rest
            // of the vector each time.

            m_globalImportList = new StylesheetComposed[importList.size()];

            for (int i = 0, j = importList.size() - 1; i < importList.size(); i++) {
                m_globalImportList[j] = (StylesheetComposed) importList.elementAt(i);
                // Build the global include list for this stylesheet.
                // This needs to be done ahead of the recomposeImports
                // because we need the info from the composed includes.
                m_globalImportList[j].recomposeIncludes(m_globalImportList[j]);
                // Calculate the number of this import.
                m_globalImportList[j--].recomposeImports();
            }
        }
        // Next, we walk the import tree and add all of the recomposable elements to the vector.
        int n = getGlobalImportCount();

        for (int i = 0; i < n; i++) {
            StylesheetComposed imported = getGlobalImport(i);
            imported.recompose(recomposableElements);
        }

        // We sort the elements into ascending order.

        QuickSort2(recomposableElements, 0, recomposableElements.size() - 1);

        // We set up the global variables that will hold the recomposed information.


        m_outputProperties = new OutputProperties(wx.xml.xalan.xml.serializer.Method.UNKNOWN);
//  m_outputProperties = new OutputProperties(Method.XML);

        m_attrSets = new HashMap();
        m_decimalFormatSymbols = new Hashtable();
        m_keyDecls = new Vector();
        m_namespaceAliasComposed = new Hashtable();
        m_templateList = new TemplateList();
        m_variables = new Vector();

        // Now we sequence through the sorted elements,
        // calling the recompose() function on each one.  This will call back into the
        // appropriate routine here to actually do the recomposition.
        // Note that we're going backwards, encountering the highest precedence items first.
        for (int i = recomposableElements.size() - 1; i >= 0; i--)
            ((ElemTemplateElement) recomposableElements.elementAt(i)).recompose(this);

        /*
         * Backing out REE again, as it seems to cause some new failures
         * which need to be investigated. -is
         */
        // This has to be done before the initialization of the compose state, because
        // eleminateRedundentGlobals will add variables to the m_variables vector, which
        // it then copied in the ComposeState constructor.

//    if(true && wx.xml.xalan.xalan.processor.TransformerFactoryImpl.m_optimize)
//    {
//          RedundentExprEliminator ree = new RedundentExprEliminator();
//          callVisitors(ree);
//          ree.eleminateRedundentGlobals(this);
//    }

        initComposeState();

        // Need final composition of TemplateList.  This adds the wild cards onto the chains.
        m_templateList.compose(this);

        // Need to clear check for properties at the same import level.
        m_outputProperties.compose(this);
        m_outputProperties.endCompose(this);

        // Now call the compose() method on every element to give it a chance to adjust
        // based on composed values.

        n = getGlobalImportCount();

        for (int i = 0; i < n; i++) {
            StylesheetComposed imported      = this.getGlobalImport(i);
            int                includedCount = imported.getIncludeCountComposed();
            for (int j = -1; j < includedCount; j++) {
                Stylesheet included = imported.getIncludeComposed(j);
                composeTemplates(included);
            }
        }
        // Attempt to register any remaining unregistered extension namespaces.
        if (m_extNsMgr != null)
            m_extNsMgr.registerUnregisteredNamespaces();

        clearComposeState();
    }

    /**
     * Call the compose function for each ElemTemplateElement.
     *
     * @param templ non-null reference to template element that will have
     *              the composed method called on it, and will have it's children's composed
     *              methods called.
     */
    void composeTemplates(ElemTemplateElement templ) throws TransformerException {

        templ.compose(this);

        for (ElemTemplateElement child = templ.getFirstChildElem();
             child != null; child = child.getNextSiblingElem()) {
            composeTemplates(child);
        }

        templ.endCompose(this);
    }

    /**
     * Add the imports in the given sheet to the working importList vector.
     * The will be added from highest import precedence to
     * least import precedence.  This is a post-order traversal of the
     * import tree as described in <a href="http://www.w3.org/TR/xslt.html#import">the
     * XSLT Recommendation</a>.
     * <p>For example, suppose</p>
     * <p>stylesheet A imports stylesheets B and C in that order;</p>
     * <p>stylesheet B imports stylesheet D;</p>
     * <p>stylesheet C imports stylesheet E.</p>
     * <p>Then the order of import precedence (highest first) is
     * A, C, E, B, D.</p>
     *
     * @param stylesheet Stylesheet to examine for imports.
     * @param addToList  <code>true</code> if this template should be added to the import list
     * @param importList The working import list.  Templates are added here in the reverse
     *                   order of priority.  When we're all done, we'll reverse this to the correct
     *                   priority in an array.
     */
    protected void addImports(Stylesheet stylesheet, boolean addToList, Vector importList) {

        // Get the direct imports of this sheet.

        int n = stylesheet.getImportCount();

        if (n > 0) {
            for (int i = 0; i < n; i++) {
                Stylesheet imported = stylesheet.getImport(i);

                addImports(imported, true, importList);
            }
        }

        n = stylesheet.getIncludeCount();

        if (n > 0) {
            for (int i = 0; i < n; i++) {
                Stylesheet included = stylesheet.getInclude(i);

                addImports(included, false, importList);
            }
        }

        if (addToList)
            importList.addElement(stylesheet);

    }

    /**
     * Get a stylesheet from the global import list.
     * TODO: JKESS PROPOSES SPECIAL-CASE FOR NO IMPORT LIST, TO MATCH COUNT.
     *
     * @param i Index of stylesheet to get from global import list
     * @return The stylesheet at the given index
     */
    public StylesheetComposed getGlobalImport(int i) {
        return m_globalImportList[i];
    }

    /**
     * Get the total number of imports in the global import list.
     *
     * @return The total number of imported stylesheets, including
     * the root stylesheet, thus the number will always be 1 or
     * greater.
     * TODO: JKESS PROPOSES SPECIAL-CASE FOR NO IMPORT LIST, TO MATCH DESCRIPTION.
     */
    public int getGlobalImportCount() {
        return (m_globalImportList != null)
               ? m_globalImportList.length
               : 1;
    }

    /**
     * Given a stylesheet, return the number of the stylesheet
     * in the global import list.
     *
     * @param sheet The stylesheet which will be located in the
     *              global import list.
     * @return The index into the global import list of the given stylesheet,
     * or -1 if it is not found (which should never happen).
     */
    public int getImportNumber(StylesheetComposed sheet) {

        if (this == sheet)
            return 0;

        int n = getGlobalImportCount();

        for (int i = 0; i < n; i++) {
            if (sheet == getGlobalImport(i))
                return i;
        }

        return -1;
    }

    /**
     * Recompose the output format object from the included elements.
     *
     * @param oprops non-null reference to xsl:output properties representation.
     */
    void recomposeOutput(OutputProperties oprops)
        throws TransformerException {

        m_outputProperties.copyFrom(oprops);
    }

    /**
     * Get the combined "xsl:output" property with the properties
     * combined from the included stylesheets.  If a xsl:output
     * is not declared in this stylesheet or an included stylesheet,
     * look in the imports.
     * Please note that this returns a reference to the OutputProperties
     * object, not a cloned object, like getOutputProperties does.
     *
     * @return non-null reference to composed output properties object.
     * @see <a href="http://www.w3.org/TR/xslt#output">output in XSLT Specification</a>
     */
    public OutputProperties getOutputComposed() {

        // System.out.println("getOutputComposed.getIndent: "+m_outputProperties.getIndent());
        // System.out.println("getOutputComposed.getIndenting: "+m_outputProperties.getIndenting());
        return m_outputProperties;
    }

    /**
     * Find out if an output method has been set by the user.
     *
     * @return Value indicating whether an output method has been set by the user
     * @xsl.usage internal
     */
    public boolean isOutputMethodSet() {
        return m_outputMethodSet;
    }

    /**
     * Recompose the attribute-set declarations.
     *
     * @param attrSet An attribute-set to add to the hashtable of attribute sets.
     */
    void recomposeAttributeSets(ElemAttributeSet attrSet) {
        ArrayList attrSetList = (ArrayList) m_attrSets.get(attrSet.getName());

        if (null == attrSetList) {
            attrSetList = new ArrayList();

            m_attrSets.put(attrSet.getName(), attrSetList);
        }

        attrSetList.add(attrSet);
    }

    /**
     * Get a list "xsl:attribute-set" properties that match the qname.
     *
     * @param name Qualified name of attribute set properties to get
     * @return A vector of attribute sets matching the given name
     * @throws ArrayIndexOutOfBoundsException
     * @see <a href="http://www.w3.org/TR/xslt#attribute-sets">attribute-sets in XSLT Specification</a>
     */
    public ArrayList getAttributeSetComposed(QName name)
        throws ArrayIndexOutOfBoundsException {
        return (ArrayList) m_attrSets.get(name);
    }

    /**
     * Recompose the decimal-format declarations.
     *
     * @param dfp A DecimalFormatProperties to add to the hashtable of decimal formats.
     */
    void recomposeDecimalFormats(DecimalFormatProperties dfp) {
        DecimalFormatSymbols oldDfs =
            (DecimalFormatSymbols) m_decimalFormatSymbols.get(dfp.getName());
        if (null == oldDfs) {
            m_decimalFormatSymbols.put(dfp.getName(), dfp.getDecimalFormatSymbols());
        } else if (!dfp.getDecimalFormatSymbols().equals(oldDfs)) {
            String themsg;
            if (dfp.getName().equals(new QName(""))) {
                // "Only one default xsl:decimal-format declaration is allowed."
                themsg = XSLMessages.createWarning(
                    XSLTErrorResources.WG_ONE_DEFAULT_XSLDECIMALFORMAT_ALLOWED,
                    new Object[0]);
            } else {
                // "xsl:decimal-format names must be unique. Name {0} has been duplicated."
                themsg = XSLMessages.createWarning(
                    XSLTErrorResources.WG_XSLDECIMALFORMAT_NAMES_MUST_BE_UNIQUE,
                    new Object[]{dfp.getName()});
            }

            error(themsg);   // Should we throw TransformerException instead?
        }

    }

    /**
     * Given a valid element decimal-format name, return the
     * decimalFormatSymbols with that name.
     * <p>It is an error to declare either the default decimal-format or
     * a decimal-format with a given name more than once (even with
     * different import precedence), unless it is declared every
     * time with the same value for all attributes (taking into
     * account any default values).</p>
     * <p>Which means, as far as I can tell, the decimal-format
     * properties are not additive.</p>
     *
     * @param name Qualified name of the decimal format to find
     * @return DecimalFormatSymbols object matching the given name or
     * null if name is not found.
     */
    public DecimalFormatSymbols getDecimalFormatComposed(QName name) {
        return (DecimalFormatSymbols) m_decimalFormatSymbols.get(name);
    }

    /**
     * Recompose the key declarations.
     *
     * @param keyDecl A KeyDeclaration to be added to the vector of key declarations.
     */
    void recomposeKeys(KeyDeclaration keyDecl) {
        m_keyDecls.addElement(keyDecl);
    }

    /**
     * Get the composed "xsl:key" properties.
     *
     * @return A vector of the composed "xsl:key" properties.
     * @see <a href="http://www.w3.org/TR/xslt#key">key in XSLT Specification</a>
     */
    public Vector getKeysComposed() {
        return m_keyDecls;
    }

    /**
     * Recompose the namespace-alias declarations.
     *
     * @param nsAlias A NamespaceAlias object to add to the hashtable of namespace aliases.
     */
    void recomposeNamespaceAliases(NamespaceAlias nsAlias) {
        m_namespaceAliasComposed.put(nsAlias.getStylesheetNamespace(),
            nsAlias);
    }

    /**
     * Get the "xsl:namespace-alias" property.
     * Return the NamespaceAlias for a given namespace uri.
     *
     * @param uri non-null reference to namespace that is to be aliased.
     * @return NamespaceAlias that matches uri, or null if no match.
     * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
     */
    public NamespaceAlias getNamespaceAliasComposed(String uri) {
        return (NamespaceAlias) ((null == m_namespaceAliasComposed)
                                 ? null : m_namespaceAliasComposed.get(uri));
    }

    /**
     * Recompose the template declarations.
     *
     * @param template An ElemTemplate object to add to the template list.
     */
    void recomposeTemplates(ElemTemplate template) {
        m_templateList.setTemplate(template);
    }

    /**
     * Accessor method to retrieve the <code>TemplateList</code> associated with
     * this StylesheetRoot.
     *
     * @return The composed <code>TemplateList</code>.
     */
    public final TemplateList getTemplateListComposed() {
        return m_templateList;
    }

    /**
     * Mutator method to set the <code>TemplateList</code> associated with this
     * StylesheetRoot.  This method should only be used by the compiler.  Normally,
     * the template list is built during the recompose process and should not be
     * altered by the user.
     *
     * @param templateList The new <code>TemplateList</code> for this StylesheetRoot.
     */
    public final void setTemplateListComposed(TemplateList templateList) {
        m_templateList = templateList;
    }

    /**
     * Get an "xsl:template" property by node match. This looks in the imports as
     * well as this stylesheet.
     *
     * @param xctxt                 non-null reference to XPath runtime execution context.
     * @param targetNode            non-null reference of node that the template must match.
     * @param mode                  qualified name of the node, or null.
     * @param quietConflictWarnings true if conflict warnings should not be reported.
     * @return reference to ElemTemplate that is the best match for targetNode, or
     * null if no match could be made.
     * @throws TransformerException
     * @see <a href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules in XSLT Specification</a>
     */
    public ElemTemplate getTemplateComposed(XPathContext xctxt,
                                            int targetNode,
                                            QName mode,
                                            boolean quietConflictWarnings,
                                            DTM dtm)
        throws TransformerException {
        return m_templateList.getTemplate(xctxt, targetNode, mode,
            quietConflictWarnings,
            dtm);
    }

    /**
     * Get an "xsl:template" property by node match. This looks in the imports as
     * well as this stylesheet.
     *
     * @param xctxt                 non-null reference to XPath runtime execution context.
     * @param targetNode            non-null reference of node that the template must match.
     * @param mode                  qualified name of the node, or null.
     * @param maxImportLevel        The maximum importCountComposed that we should consider or -1
     *                              if we should consider all import levels.  This is used by apply-imports to
     *                              access templates that have been overridden.
     * @param endImportLevel        The count of composed imports
     * @param quietConflictWarnings true if conflict warnings should not be reported.
     * @return reference to ElemTemplate that is the best match for targetNode, or
     * null if no match could be made.
     * @throws TransformerException
     * @see <a href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules in XSLT Specification</a>
     */
    public ElemTemplate getTemplateComposed(XPathContext xctxt,
                                            int targetNode,
                                            QName mode,
                                            int maxImportLevel, int endImportLevel,
                                            boolean quietConflictWarnings,
                                            DTM dtm)
        throws TransformerException {
        return m_templateList.getTemplate(xctxt, targetNode, mode,
            maxImportLevel, endImportLevel,
            quietConflictWarnings,
            dtm);
    }

    /**
     * Get an "xsl:template" property. This looks in the imports as
     * well as this stylesheet.
     *
     * @param qname non-null reference to qualified name of template.
     * @return reference to named template, or null if not found.
     * @see <a href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules in XSLT Specification</a>
     */
    public ElemTemplate getTemplateComposed(QName qname) {
        return m_templateList.getTemplate(qname);
    }

    /**
     * Recompose the top level variable and parameter declarations.
     *
     * @param elemVar A top level variable or parameter to be added to the Vector.
     */
    void recomposeVariables(ElemVariable elemVar) {
        // Don't overide higher priority variable
        if (getVariableOrParamComposed(elemVar.getName()) == null) {
            elemVar.setIsTopLevel(true);        // Mark as a top-level variable or param
            elemVar.setIndex(m_variables.size());
            m_variables.addElement(elemVar);
        }
    }

    /**
     * Get an "xsl:variable" property.
     *
     * @param qname Qualified name of variable or param
     * @return The ElemVariable with the given qualified name
     * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
     */
    public ElemVariable getVariableOrParamComposed(QName qname) {
        if (null != m_variables) {
            int n = m_variables.size();

            for (int i = 0; i < n; i++) {
                ElemVariable var = (ElemVariable) m_variables.elementAt(i);
                if (var.getName().equals(qname))
                    return var;
            }
        }

        return null;
    }

    /**
     * Get all global "xsl:variable" properties in scope for this stylesheet.
     *
     * @return Vector of all variables and params in scope
     * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
     */
    public Vector getVariablesAndParamsComposed() {
        return m_variables;
    }

    /**
     * Recompose the strip-space and preserve-space declarations.
     *
     * @param wsi A WhiteSpaceInfo element to add to the list of WhiteSpaceInfo elements.
     */
    void recomposeWhiteSpaceInfo(WhiteSpaceInfo wsi) {
        if (null == m_whiteSpaceInfoList)
            m_whiteSpaceInfoList = new TemplateList();

        m_whiteSpaceInfoList.setTemplate(wsi);
    }

    /**
     * Check to see if the caller should bother with check for
     * whitespace nodes.
     *
     * @return Whether the caller should bother with check for
     * whitespace nodes.
     */
    public boolean shouldCheckWhitespace() {
        return null != m_whiteSpaceInfoList;
    }

    /**
     * Get information about whether or not an element should strip whitespace.
     *
     * @param support       The XPath runtime state.
     * @param targetElement Element to check
     * @return WhiteSpaceInfo for the given element
     * @throws TransformerException
     * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
     */
    public WhiteSpaceInfo getWhiteSpaceInfo(
        XPathContext support, int targetElement, DTM dtm) throws TransformerException {

        if (null != m_whiteSpaceInfoList)
            return (WhiteSpaceInfo) m_whiteSpaceInfoList.getTemplate(support,
                targetElement, null, false, dtm);
        else
            return null;
    }

    /**
     * Get information about whether or not an element should strip whitespace.
     *
     * @param support       The XPath runtime state.
     * @param targetElement Element to check
     * @return true if the whitespace should be stripped.
     * @throws TransformerException
     * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
     */
    public boolean shouldStripWhiteSpace(
        XPathContext support, int targetElement) throws TransformerException {
        if (null != m_whiteSpaceInfoList) {
            while (DTM.NULL != targetElement) {
                DTM dtm = support.getDTM(targetElement);
                WhiteSpaceInfo info = (WhiteSpaceInfo) m_whiteSpaceInfoList.getTemplate(support,
                    targetElement, null, false, dtm);
                if (null != info)
                    return info.getShouldStripSpace();

                int parent = dtm.getParent(targetElement);
                if (DTM.NULL != parent && DTM.ELEMENT_NODE == dtm.getNodeType(parent))
                    targetElement = parent;
                else
                    targetElement = DTM.NULL;
            }
        }
        return false;
    }

    /**
     * Get information about whether or not whitespace can be stripped.
     *
     * @return true if the whitespace can be stripped.
     * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
     */
    public boolean canStripWhiteSpace() {
        return (null != m_whiteSpaceInfoList);
    }

    /**
     * Get the default template for text.
     *
     * @return the default template for text.
     * @xsl.usage advanced
     */
    public final ElemTemplate getDefaultTextRule() {
        return m_defaultTextRule;
    }

    /**
     * Get the default template for elements.
     *
     * @return the default template for elements.
     * @xsl.usage advanced
     */
    public final ElemTemplate getDefaultRule() {
        return m_defaultRule;
    }

    /**
     * Get the default template for a root node.
     *
     * @return The default template for a root node.
     * @xsl.usage advanced
     */
    public final ElemTemplate getDefaultRootRule() {
        return m_defaultRootRule;
    }

    /**
     * Get the default template for a root node.
     *
     * @return The default template for a root node.
     * @xsl.usage advanced
     */
    public final ElemTemplate getStartRule() {
        return m_startRule;
    }

    /**
     * Create the default rule if needed.
     *
     * @throws TransformerException
     */
    private void initDefaultRule(ErrorListener errorListener) throws TransformerException {

        // Then manufacture a default
        m_defaultRule = new ElemTemplate();

        m_defaultRule.setStylesheet(this);

        XPath defMatch = new XPath("*", this, this, XPath.MATCH, errorListener);

        m_defaultRule.setMatch(defMatch);

        ElemApplyTemplates childrenElement = new ElemApplyTemplates();

        childrenElement.setIsDefaultTemplate(true);
        childrenElement.setSelect(m_selectDefault);
        m_defaultRule.appendChild(childrenElement);

        m_startRule = m_defaultRule;

        // -----------------------------
        m_defaultTextRule = new ElemTemplate();

        m_defaultTextRule.setStylesheet(this);

        defMatch = new XPath("text() | @*", this, this, XPath.MATCH, errorListener);

        m_defaultTextRule.setMatch(defMatch);

        ElemValueOf elemValueOf = new ElemValueOf();

        m_defaultTextRule.appendChild(elemValueOf);

        XPath selectPattern = new XPath(".", this, this, XPath.SELECT, errorListener);

        elemValueOf.setSelect(selectPattern);

        //--------------------------------
        m_defaultRootRule = new ElemTemplate();

        m_defaultRootRule.setStylesheet(this);

        defMatch = new XPath("/", this, this, XPath.MATCH, errorListener);

        m_defaultRootRule.setMatch(defMatch);

        childrenElement = new ElemApplyTemplates();

        childrenElement.setIsDefaultTemplate(true);
        m_defaultRootRule.appendChild(childrenElement);
        childrenElement.setSelect(m_selectDefault);
    }

    /**
     * This is a generic version of C.A.R Hoare's Quick Sort
     * algorithm.  This will handle arrays that are already
     * sorted, and arrays with duplicate keys.  It was lifted from
     * the NodeSorter class but should probably be eliminated and replaced
     * with a call to Collections.sort when we migrate to Java2.<BR>
     * <p>
     * If you think of a one dimensional array as going from
     * the lowest index on the left to the highest index on the right
     * then the parameters to this function are lowest index or
     * left and highest index or right.  The first time you call
     * this function it will be with the parameters 0, a.length - 1.
     *
     * @param v   a vector of ElemTemplateElement elements
     * @param lo0 left boundary of partition
     * @param hi0 right boundary of partition
     */

    private void QuickSort2(Vector v, int lo0, int hi0) {
        int lo = lo0;
        int hi = hi0;

        if (hi0 > lo0) {
            // Arbitrarily establishing partition element as the midpoint of
            // the array.
            ElemTemplateElement midNode = (ElemTemplateElement) v.elementAt((lo0 + hi0) / 2);

            // loop through the array until indices cross
            while (lo <= hi) {
                // find the first element that is greater than or equal to
                // the partition element starting from the left Index.
                while ((lo < hi0) && (((ElemTemplateElement) v.elementAt(lo)).compareTo(midNode) < 0)) {
                    ++lo;
                } // end while

                // find an element that is smaller than or equal to
                // the partition element starting from the right Index.
                while ((hi > lo0) && (((ElemTemplateElement) v.elementAt(hi)).compareTo(midNode) > 0)) {
                    --hi;
                }

                // if the indexes have not crossed, swap
                if (lo <= hi) {
                    ElemTemplateElement node = (ElemTemplateElement) v.elementAt(lo);
                    v.setElementAt(v.elementAt(hi), lo);
                    v.setElementAt(node, hi);

                    ++lo;
                    --hi;
                }
            }

            // If the right index has not reached the left side of array
            // must now sort the left partition.
            if (lo0 < hi) {
                QuickSort2(v, lo0, hi);
            }

            // If the left index has not reached the right side of array
            // must now sort the right partition.
            if (lo < hi0) {
                QuickSort2(v, lo, hi0);
            }
        }
    } // end QuickSort2  */

    /**
     * Initialize a new ComposeState.
     */
    void initComposeState() {
        m_composeState = new ComposeState();
    }

    /**
     * Return class to track state global state during the compose() operation.
     *
     * @return ComposeState reference, or null if endCompose has been called.
     */
    ComposeState getComposeState() {
        return m_composeState;
    }

    /**
     * Clear the compose state.
     */
    private void clearComposeState() {
        m_composeState = null;
    }

    /**
     * This internal method allows the setting of the java class
     * to handle the extension function (if other than the default one).
     *
     * @xsl.usage internal
     */
    public String setExtensionHandlerClass(String handlerClassName) {
        String oldvalue = m_extensionHandlerClass;
        m_extensionHandlerClass = handlerClassName;
        return oldvalue;
    }

    /**
     * @xsl.usage internal
     */
    public String getExtensionHandlerClass() {
        return m_extensionHandlerClass;
    }

    /**
     * @return Optimization flag
     */
    public boolean getOptimizer() {
        return m_optimizer;
    }

    /**
     * @param b Optimization flag
     */
    public void setOptimizer(boolean b) {
        m_optimizer = b;
    }

    /**
     * @return Incremental flag
     */
    public boolean getIncremental() {
        return m_incremental;
    }

    /**
     * @param b Incremental flag
     */
    public void setIncremental(boolean b) {
        m_incremental = b;
    }

    /**
     * @return source location flag
     */
    public boolean getSource_location() {
        return m_source_location;
    }

    /**
     * @param b Source location flag
     */
    public void setSource_location(boolean b) {
        m_source_location = b;
    }

    /**
     * Class to track state global state during the compose() operation.
     */
    class ComposeState {
        IntStack m_marks = new IntStack();
        private ExpandedNameTable m_ent = new ExpandedNameTable();
        /**
         * A Vector of the current params and QNames within the current template.
         * Set by ElemTemplate and used by ProcessorVariable.
         */
        private java.util.Vector m_variableNames = new java.util.Vector();
        private int m_maxStackFrameSize;

        ComposeState() {
            int size = m_variables.size();
            for (int i = 0; i < size; i++) {
                ElemVariable ev = (ElemVariable) m_variables.elementAt(i);
                m_variableNames.addElement(ev.getName());
            }

        }

        /**
         * Given a qualified name, return an integer ID that can be
         * quickly compared.
         *
         * @param qname a qualified name object, must not be null.
         * @return the expanded-name id of the qualified name.
         */
        public int getQNameID(QName qname) {

            return m_ent.getExpandedTypeID(qname.getNamespace(),
                qname.getLocalName(),
                // The type doesn't matter for our
                // purposes.
                wx.xml.xalan.xml.dtm.DTM.ELEMENT_NODE);
        }

        /**
         * Add the name of a qualified name within the template.  The position in
         * the vector is its ID.
         *
         * @param qname A qualified name of a param or variable, should be non-null.
         * @return the index where the variable was added.
         */
        int addVariableName(final wx.xml.xalan.xml.utils.QName qname) {
            int pos = m_variableNames.size();
            m_variableNames.addElement(qname);
            int frameSize = m_variableNames.size() - getGlobalsSize();
            if (frameSize > m_maxStackFrameSize)
                m_maxStackFrameSize++;
            return pos;
        }

        void resetStackFrameSize() {
            m_maxStackFrameSize = 0;
        }

        int getFrameSize() {
            return m_maxStackFrameSize;
        }

        /**
         * Get the current size of the stack frame.  Use this to record the position
         * in a template element at startElement, so that it can be popped
         * at endElement.
         */
        int getCurrentStackFrameSize() {
            return m_variableNames.size();
        }

        /**
         * Set the current size of the stack frame.
         */
        void setCurrentStackFrameSize(int sz) {
            m_variableNames.setSize(sz);
        }

        int getGlobalsSize() {
            return m_variables.size();
        }

        void pushStackMark() {
            m_marks.push(getCurrentStackFrameSize());
        }

        void popStackMark() {
            int mark = m_marks.pop();
            setCurrentStackFrameSize(mark);
        }

        /**
         * Get the Vector of the current params and QNames to be collected
         * within the current template.
         *
         * @return A reference to the vector of variable names.  The reference
         * returned is owned by this class, and so should not really be mutated, or
         * stored anywhere.
         */
        java.util.Vector getVariableNames() {
            return m_variableNames;
        }

    }

}
